#include "task.h"

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "builtin.h"
#include "stringtokenizer.h"

struct Task {
    BuiltInCommand built_in;
    char * executable;
    char ** argv;
    int stdin, stdout;
};

char * resolve_executable_from_path( const char * command )
{
    const char * temp  = getenv( "PATH" );
    char * path = malloc( 1 + sizeof(char) * strlen( temp ) );
    strcpy( path, temp );

    char * pch = strtok( path, ":" );

    while ( pch != NULL ) {
        DIR * directory = opendir( pch );

        if ( directory != NULL ) {
            struct dirent * entry;

            while ( (entry = readdir( directory ) ) ) {
                if ( strcmp( entry->d_name, command ) == 0 ) {
                    char * ret = malloc( 1 + sizeof(char) * PATH_MAX );
                    sprintf( ret, "%s/%s", pch, command );
                    return ret;
                }
            }
        }

        pch = strtok( NULL, ":" );
    }

    printf("-brash: %s command not found\n", command);
    return NULL;
}

char * resolve_executable( const char * file )
{
    struct stat s;
    if ( stat( file, &s) != 0 ) {
        printf( "-brash: %s file not found\n", file );
        return NULL;
    }

    char * ret = malloc( 1 + sizeof(char) * PATH_MAX );
    realpath( file, ret );

    return ret;
}

struct Task * task_create( const char * command, int stdin, int stdout )
{
    struct Task * ret = malloc( sizeof(struct Task) );

//-------------initialize argv-----------------------

    struct StringTokens * tokens = tokens_create( command, " " );

    ret->argv = malloc( sizeof(char *) * tokens_size( tokens ) + 1 );

    for ( size_t i = 0; i < tokens_size( tokens ); i++ )
        ret->argv[i] = strdup( tokens_get( tokens, i ) );

    ret->argv[ tokens_size( tokens ) ] = NULL;

    tokens_free( tokens );

//---------initialize built_in-----------------------------

    if ( ret->argv[0] == NULL ) {
        ret->executable = NULL;
        ret->built_in = NULL;
        return ret;
    }

    ret->built_in = get_built_in( ret->argv[0] );

//-----------initialize executable-----------------------

    if ( ret->built_in != NULL ) {
        ret->executable = NULL;
        return ret;
    }

    switch ( ret->argv[0][0] )
    {
        case '/':
        case '.':
            ret->executable = resolve_executable( ret->argv[0] );
            break;
        default:
            ret->executable = resolve_executable_from_path( ret->argv[0] );
            break;
    }

    return ret;
}

bool task_fork_needed( struct Task * task )
{
    return ( task->built_in == NULL );
}

void task_run_forked( struct Task * task )
{
    dup2( task->stdin, STDIN_FILENO );
    dup2( task->stdout, STDOUT_FILENO );

    if ( task->stdin != STDIN_FILENO )
        close( task->stdin );
    if ( task->stdout != STDOUT_FILENO )
        close( task->stdout );

    if ( task->built_in != NULL ) {
        task->built_in( task->argv );
        exit(0);
    } else {
        execv( task->executable, task->argv );
        exit(1);
    }
}

void task_run_unforked( struct Task * task )
{
//    dup2( task->stdin, STDIN_FILENO );
//    dup2( task->stdout, STDOUT_FILENO );

    if ( task->built_in != NULL ) {
        task->built_in( task->argv );
    } else {
        execv( task->executable, task->argv );
        exit(1);
    }
}

void task_free( struct Task * task )
{
    for ( size_t i = 0; task->argv[i] != NULL; i++ )
        free( task->argv[i] );

    if ( task->stdin != STDIN_FILENO )
        close( task->stdin );

    if ( task->stdout != STDOUT_FILENO )
        close( task->stdout );

    free( task->argv );
    free( task->executable );
    free( task );
}
